﻿/**
 * Copyright (C) 2004-2009 D3Systems, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA 
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Diagnostics;
using System.Collections;

namespace OMGServer
{
    /// <summary>
    /// Connection Manager Object that will be in charge of 
    /// handling the server socket and handing it off and assigning 
    /// it to their respective client socket handlers.
    /// </summary>
    class ConnectionMgr
    {
        //All about threads
        /// <summary>
        /// ManualResetEvent used to synchronize the connectivity process to avoid certain race conditions.
        /// </summary>
        /// <remarks>This boolean if false will stop the infinite loop of the server socket.</remarks>
        private static ManualResetEvent connectDone = new ManualResetEvent(false);

        // Contains the IP Address and Port wherein the Server will be listening on
        /// <summary>
        /// IPEndPoint of the Socket Server. Indicates the Incoming Port.
        /// </summary>
        System.Net.IPEndPoint ipLocal = null;

        // Socket object
        /// <summary>
        /// Server Socket Object used to listen and handle the initial client socket connection.
        /// </summary>
        public Socket socketListener;

        // Event Logger for Debugging Purposes
        /// <summary>
        /// Reference to the Event Log object used to track and debug the application.
        /// </summary>
        System.Diagnostics.EventLog logger;


        /// <summary>
        /// Array List containing the Sessions.
        /// </summary>
        ArrayList arrayClientSockets;

        /// <summary>
        /// Flag indicating that we are accepting incoming connections.
        /// </summary>
        public Boolean isRunning;

        /// <summary>
        /// ConnectionMgr Constructor.
        /// </summary>
        public ConnectionMgr(EventLog logger)
        {
            arrayClientSockets = new ArrayList();

            ipLocal = new System.Net.IPEndPoint(System.Net.IPAddress.Any, 8222);
            
            // Initialize Logger
            this.logger = logger;
            
            isRunning = true;
            InitializeThreads();
        }


        /// <summary>
        /// Intializes and Sets the worker threads. This method also starts everything.
        /// </summary>
        public void InitializeThreads()
        {
            try
            {
                // Instantiate the Server's Socket Listener ...
                socketListener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                // Create a ThreadStart object to handle the Server Socket Listener ... Reference the Start Method which will start the listener.
                ThreadStart threadStart1 = new ThreadStart(Start);

                // Create a Thread to handle the ThreadStart
                Thread thread = new Thread(threadStart1);

                // Set it as a background thread.
                thread.IsBackground = true;

                // Start the Thread
                thread.Start();
            }
            catch (Exception ex)
            {
                logger.WriteEntry("Error Initializing Threads: " + ex.Message);
            }
        }

        /// <summary>
        /// Start listening for client sockets.
        /// </summary>
        private void Start()
        {
            // Bind the socket to the local endpoint and listen for incoming connections.
            try
            {
                
                
                //bind to local IP Address ...
                socketListener.Bind(ipLocal);
                //Start Listening
                socketListener.Listen(1000);

                // Let's loop this forever until we stop the service.
                while (isRunning)
                {
                    logger.WriteEntry("Waiting for Incoming Connection:");

                    // Reset the ManualResetEvent before we pause the thread ...
                    connectDone.Reset();

                    // Begin Socket Acceptor
                    socketListener.BeginAccept(new AsyncCallback(AcceptConnection), null);                    

                    // Pause the Thread
                    connectDone.WaitOne();
                }

            }
            catch (Exception ex)
            {
                logger.WriteEntry("Error Starting Connection Manager: " + ex.Message);
            }
        }

        /// <summary>
        /// Call back method used in the Start() method. Triggered when there is an incoming socket connection.
        /// </summary>
        /// <remarks>Called when there is an incoming socket connection.</remarks>
        /// <param name="ar"></param>
        private void AcceptConnection(IAsyncResult ar)
        {
            logger.WriteEntry("Someone Connected!!!");

            // We have connected ... let the Main Thread continue on ...
            connectDone.Set();

            // Get the Socket of the Client
            Socket clientSocket = socketListener.EndAccept(ar);

            // Create the SocketPacket to handle the communication exchange with the client
            SocketPacket packet = new SocketPacket(logger, new AsyncCallback(ContinueDisconnect));

            // Store the Socket Object inside the Socket Packet Object
            packet.clientSocket = clientSocket;

            // Add the SocketPacket Object into the array for easy retrieval later
            arrayClientSockets.Add(packet);

            // Let's wait for the Data to come in ...
            packet.WaitForData();

        }

        /// <summary>
        /// Call back method that will be called when a client socket has disconnected.
        /// </summary>
        private void ContinueDisconnect(IAsyncResult ar)
        {
            logger.WriteEntry("Disconnecting Socket ...");

            // Retrieve the packet ...
            SocketPacket packet = (SocketPacket)ar.AsyncState;


            // Remove it from the Array 
            arrayClientSockets.Remove(packet);
        }
    }
}
